package com.luo.gmall.index.aspect;

import com.alibaba.fastjson.JSON;
import com.luo.gmall.index.config.GmallCache;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author Zoro
 * @date 2021年11月17
 */
@Aspect
@Component
public class GmallAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RBloomFilter<Object> bloomFilter;

    @Around("@annotation(com.luo.gmall.index.config.GmallCache)")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 获取切点方法的签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法对象
        Method method = signature.getMethod();
        // 获取方法上指定注解的对象
        GmallCache annotation = method.getAnnotation(GmallCache.class);
        // 获取注解中的前缀
        String prefix = annotation.prefix();
        // 获取方法的参数
        Object[] args = joinPoint.getArgs();
        String params = StringUtils.join(args, ",");
        // 获取方法的返回值类型
        Class<?> returnType = method.getReturnType();
        String key = prefix + params;
        // 查找布隆过滤器中的数据，没有的话直接返回
        if (StringUtils.isNotBlank(params)) {
            if (!bloomFilter.contains(key)) {
                return null;
            }
        }

        // 拦截前代码块：判断缓存中有没有
        String json = redisTemplate.opsForValue().get(key);
        // 判断缓存中的数据是否为空
        if (StringUtils.isNotBlank(json)) {
            return JSON.parseObject(json, returnType);
        }
        // 没有，加分布式锁
        RLock lock = redissonClient.getLock(annotation.lock() + params);
        lock.lock();
        try {
            // 判断缓存中有没有，有直接返回(加锁的过程中，别的请求可能已经把数据放入缓存)
            String json2 = redisTemplate.opsForValue().get(key);
            // 判断缓存中的数据是否为空
            if (StringUtils.isNotBlank(json2)) {
                return JSON.parseObject(json2, returnType);
            }
            // 执行目标方法
            Object result = joinPoint.proceed();
            // 拦截后代码块：放入缓存 释放分布锁

            redisTemplate.opsForValue().set(key, JSON.toJSONString(result), annotation.timeout() + new Random().nextInt(annotation.random()), TimeUnit.MINUTES);
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return null;
    }
}
